﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;

namespace InductBase
{
    [TypeConverter(typeof(InductBase.Helpers.BasicTypesConverter))]
    public class Rec
    {
        private Vec2 _min, _max;

        public double Area { get { return Width * Height; } }

        public Vec2 Min { get { return _min; } set { _min = value; } }
        public Vec2 Max { get { return _max; } set { _max = value; } }

        public Rec()
        {
            _min = new Vec2(0, 0);
            _max = new Vec2(0, 0);
        }

        public Rec(double x, double y, double w, double h)
        {
            _min = new Vec2(x, y);
            _max = new Vec2(x + w, y + h);
        }

        private Rec(Vec2 min, Vec2 max)
        {
            this._min = min.Copy();
            this._max = max.Copy();
        }

        public static Rec FromLocationSize(Vec2 location, Vec2 size)
        {
            Rec res = new Rec(location, location + size);
            return res;
        }

        public static Rec RecFromMinMax(Vec2 min, Vec2 max)
        {
            Rec res = new Rec(min, max);
            return res;
        }

        public Rec GetPositiveRectangle()
        {
            Vec2 nmax = new Vec2(
                _min.X > _max.X ? _min.X : _max.X,
                _min.Y > _max.Y ? _min.Y : _max.Y);

            Vec2 nmin = new Vec2(
                _min.X < _max.X ? _min.X : _max.X,
                _min.Y < _max.Y ? _min.Y : _max.Y);

            return new Rec(nmin, nmax);
        }

        public Rectangle ToRectangle() { return new Rectangle((int)_min.X, (int)_min.Y, (int)Size.X, (int)Size.Y); }
        public RectangleF ToRectangleF() { return new RectangleF((float)_min.X, (float)_min.Y, (float)Size.X, (float)Size.Y); }

        public Vec2 Size
        {
            get
            {
                return _max - _min;
            }

            set
            {
                _max = _min + value;
            }
        }

        public Vec2 Location
        {
            get
            {
                return _min;
            }

            set
            {
                var sz = Size;
                _min = value;
                _max = _min + sz;
            }
        }

        public Vec2 Center
        {
            get
            {
                return _min + (_max - _min) * 0.5;
            }

            set
            {
                Vec2 size = Size;
                _min = value - size / 2;
                _max = value + size / 2;
            }
        }                

        public static implicit operator RectangleF(Rec value)
        {
            return value.ToRectangleF();
        }

        public static implicit operator Rec(RectangleF value)
        {
            return new Rec(value.X, value.Y, value.Width, value.Height);
        }

        public static bool operator ==(Rec a, Rec b)
        {
            if (ReferenceEquals(a, b)) return true;

            if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
                return ReferenceEquals(a, b);

            return a._min == b._min && a._max == b._max;
        }

        public static bool operator !=(Rec a, Rec b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            Rec other = (Rec)obj;
            return this == obj;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public bool Contains(Vec2 point)
        {
            if (point.X < _min.X) return false;
            if (point.Y < _min.Y) return false;
            if (point.X > _max.X) return false;
            if (point.Y > _max.Y) return false;

            return true;
        }

        public bool Intersects(Rec other)
        {
            if (this.Right <= other.Left) return false;
            if (this.Bottom <= other.Top) return false;
            if (this.Top >= other.Bottom) return false;
            if (this.Left >= other.Right) return false;

            return true;
        }

        public bool FixedIntersects(Rec other)
        {
            var a = this.GetPositiveRectangle();
            var b = other.GetPositiveRectangle();

            if (a.Right <= b.Left) return false;
            if (a.Bottom <= b.Top) return false;
            if (a.Top >= b.Bottom) return false;
            if (a.Left >= b.Right) return false;

            return true;
        }

        public static Rec FromRectangle(Rectangle prototype)
        {
            return new Rec(prototype.X, prototype.Y, prototype.Width, prototype.Height);
        }

        public static Rec FromRectangleF(RectangleF prototype)
        {
            return new Rec(prototype.X, prototype.Y, prototype.Width, prototype.Height);
        }


        public Rec ZoomedIn(Rec other)
        {
            Rec res = this.Copy();
            Vec2 scale = other.Size/res.Size;
            res.Size = res.Size * Math.Min(scale.X, scale.Y);
            res.Center = other.Center;
            return res;
        }

        public Vec2 CenterTop
        {
            get
            {
                return LeftUpperCorner + new Vec2(Width*0.5,0);
            }
        }

        public Vec2 CenterBottom
        {
            get
            {
                return LeftBottomCorner + new Vec2(Width * 0.5, 0);
            }
        }

        public Vec2 LeftUpperCorner
        {
            get
            {
                return Min.Copy();
            }

            private set { }
        }

        public Vec2 RightUpperCorner
        {
            get
            {
                return new Vec2(Max.X, Min.Y);
            }

            private set { }
        }

        public Vec2 LeftBottomCorner
        {
            get
            {
                return new Vec2(Min.X, Max.Y);
            }

            private set { }
        }

        public Vec2 RightBottomCorner
        {
            get
            {
                return new Vec2(Max.X, Max.Y);
            }

            private set { }
        }

        public double Left
        {
            get { return Min.X; }
            set { Location = new Vec2(value, Location.Y); }
        }

        public double Right
        {
            get { return Max.X; }
            set { Location = new Vec2(value - Size.X, Location.Y); }
        }

        public double Top
        {
            get { return Min.Y; }
            set { Location = new Vec2(Location.X, value); }
        }

        public double Bottom
        {
            get { return Max.Y; }
            set { Location = new Vec2(Location.X, value - Size.Y); }
        }


        public double Width
        {
            get { return _max.X - _min.X; }
            set { _max.X = _min.X + value; }
        }

        public double Height
        {
            get { return _max.Y - _min.Y; }
            set { _max.Y = _min.Y + value; }
        }


        public double X
        {
            get { return Left; }
            set { Left = value; }
        }

        public double Y
        {
            get { return Top; }
            set { Top = value; }
        }

        public Rec Copy()
        {
            return new Rec(_min.Copy(), _max.Copy());
        }

        public Rec Shrink(int margin)
        {
            return Rec.FromLocationSize(
                this.Location + new Vec2(margin, margin),
                this.Size - 2*(new Vec2(margin, margin)));
        }

        public override string ToString()
        {
            return Location.ToString() + "_" + Size.ToString();
        }

        static public Rec Parse(string inputString)
        {
            if (inputString == String.Empty) return null;

            var args = inputString.Split('_');
            return Rec.FromLocationSize(Vec2.Parse(args[0]), Vec2.Parse(args[1]));
        }

        public static explicit operator Rec(String value)
        {
            return Rec.Parse(value);
        }        
    }
}
